package cn.nexd.map.rendering.core.componet.map;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Picture;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.Shader.TileMode;
import android.text.TextUtils;
import android.util.FloatMath;
import android.util.Log;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import cn.nexd.map.rendering.core.componet.data.Feature;
import cn.nexd.map.rendering.core.componet.data.FloorsFeature;
import cn.nexd.map.rendering.core.componet.data.Node;
import cn.nexd.map.rendering.core.helper.BitmapHelper;

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
 * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
 * to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
 * License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
 * applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */

/**
 * @author Larva Labs, LLC
 */
public class SVGParser {

    static final String TAG = "SVGAndroid";

    private static boolean DISALLOW_DOCTYPE_DECL = true;


//    /**
//     * Parses a single SVG path and returns it as a
//     * <code>android.graphics.Path</code> object. An example path is
//     * <code>M250,150L150,350L350,350Z</code>, which draws a triangle.
//     *
//     * @param pathString the SVG path, see the specification <a
//     *                   href="http://www.w3.org/TR/SVG/paths.html">here</a>.
//     */
//    public static Path parsePath(String pathString) {
//        return doPath(pathString);
//    }

    static SVG parse(InputSource data, SVGHandler handler) throws SVGParseException {
        try {
            final Picture picture = new Picture();
            handler.setPicture(picture);

            SAXParserFactory spf = SAXParserFactory.newInstance();
            SAXParser sp = spf.newSAXParser();
            XMLReader xr = sp.getXMLReader();
            xr.setContentHandler(handler);
            xr.setFeature("http://xml.org/sax/features/validation", false);
            if (DISALLOW_DOCTYPE_DECL) {
                try {
                    xr.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
                } catch (SAXNotRecognizedException e) {
                    DISALLOW_DOCTYPE_DECL = false;
                }
            }
            xr.parse(data);

            SVG result = new SVG(picture, handler);
            // Skip bounds if it was an empty pic
            if (!Float.isInfinite(handler.limits.top)) {
                result.setLimits(handler.limits);
            }
            return result;
        } catch (Exception e) {
            Log.e(TAG, "Failed to parse SVG.", e);
            throw new SVGParseException(e);
        }
    }

    private static NumberParse parseNumbers(String s) {
        // Util.debug("Parsing numbers from: '" + s + "'");
        int n = s.length();
        int p = 0;
        ArrayList<Float> numbers = new ArrayList<Float>();
        boolean skipChar = false;
        boolean prevWasE = false;
        for (int i = 1; i < n; i++) {
            if (skipChar) {
                skipChar = false;
                continue;
            }
            char c = s.charAt(i);
            switch (c) {
                // This ends the parsing, as we are on the next element
                case 'M':
                case 'm':
                case 'Z':
                case 'z':
                case 'L':
                case 'l':
                case 'H':
                case 'h':
                case 'V':
                case 'v':
                case 'C':
                case 'c':
                case 'S':
                case 's':
                case 'Q':
                case 'q':
                case 'T':
                case 't':
                case 'a':
                case 'A':
                case ')': {
                    String str = s.substring(p, i);
                    if (str.trim().length() > 0) {
                        // Util.debug("  Last: " + str);
                        Float f = Float.parseFloat(str);
                        numbers.add(f);
                    }
                    p = i;
                    return new NumberParse(numbers, p);
                }
                case '-':
                    // Allow numbers with negative exp such as 7.23e-4
                    if (prevWasE) {
                        prevWasE = false;
                        break;
                    }
                    // fall-through
                case '\n':
                case '\t':
                case ' ':
                case ',': {
                    String str = s.substring(p, i);
                    // Just keep moving if multiple whitespace
                    if (str.trim().length() > 0) {
                        // Util.debug("  Next: " + str);
                        Float f = Float.parseFloat(str);
                        numbers.add(f);
                        if (c == '-') {
                            p = i;
                        } else {
                            p = i + 1;
                            skipChar = true;
                        }
                    } else {
                        p++;
                    }
                    prevWasE = false;
                    break;
                }
                case 'e':
                    prevWasE = true;
                    break;
                default:
                    prevWasE = false;
            }
        }

        String last = s.substring(p);
        if (last.length() > 0) {
            // Util.debug("  Last: " + last);
            try {
                numbers.add(Float.parseFloat(last));
            } catch (NumberFormatException nfe) {
                // Just white-space, forget it
            }
            p = s.length();
        }
        return new NumberParse(numbers, p);
    }

    private static final Pattern TRANSFORM_SEP = Pattern.compile("[\\s,]*");

    /**
     * Parse a list of transforms such as: foo(n,n,n...) bar(n,n,n..._ ...)
     * Delimiters are whitespaces or commas
     */
    private static Matrix parseTransform(String s) {
        Matrix matrix = new Matrix();
        while (true) {
            parseTransformItem(s, matrix);
            // Log.i(TAG, "Transformed: (" + s + ") " + matrix);
            final int rparen = s.indexOf(")");
            if (rparen > 0 && s.length() > rparen + 1) {
                s = TRANSFORM_SEP.matcher(s.substring(rparen + 1)).replaceFirst("");
            } else {
                break;
            }
        }
        return matrix;
    }

    private static Matrix parseTransformItem(String s, Matrix matrix) {
        if (s.startsWith("matrix(")) {
            NumberParse np = parseNumbers(s.substring("matrix(".length()));
            if (np.numbers.size() == 6) {
                Matrix mat = new Matrix();
                mat.setValues(new float[]{
                        // Row 1
                        np.numbers.get(0), np.numbers.get(2), np.numbers.get(4),
                        // Row 2
                        np.numbers.get(1), np.numbers.get(3), np.numbers.get(5),
                        // Row 3
                        0, 0, 1,});
                matrix.preConcat(mat);
            }
        } else if (s.startsWith("translate(")) {
            NumberParse np = parseNumbers(s.substring("translate(".length()));
            if (np.numbers.size() > 0) {
                float tx = np.numbers.get(0);
                float ty = 0;
                if (np.numbers.size() > 1) {
                    ty = np.numbers.get(1);
                }
                matrix.preTranslate(tx, ty);
            }
        } else if (s.startsWith("scale(")) {
            NumberParse np = parseNumbers(s.substring("scale(".length()));
            if (np.numbers.size() > 0) {
                float sx = np.numbers.get(0);
                float sy = sx;
                if (np.numbers.size() > 1) {
                    sy = np.numbers.get(1);
                }
                matrix.preScale(sx, sy);
            }
        } else if (s.startsWith("skewX(")) {
            NumberParse np = parseNumbers(s.substring("skewX(".length()));
            if (np.numbers.size() > 0) {
                float angle = np.numbers.get(0);
                matrix.preSkew((float) Math.tan(angle), 0);
            }
        } else if (s.startsWith("skewY(")) {
            NumberParse np = parseNumbers(s.substring("skewY(".length()));
            if (np.numbers.size() > 0) {
                float angle = np.numbers.get(0);
                matrix.preSkew(0, (float) Math.tan(angle));
            }
        } else if (s.startsWith("rotate(")) {
            NumberParse np = parseNumbers(s.substring("rotate(".length()));
            if (np.numbers.size() > 0) {
                float angle = np.numbers.get(0);
                float cx = 0;
                float cy = 0;
                if (np.numbers.size() > 2) {
                    cx = np.numbers.get(1);
                    cy = np.numbers.get(2);
                }
                matrix.preTranslate(-cx, -cy);
                matrix.preRotate(angle);
                matrix.preTranslate(cx, cy);
            }
        } else {
            Log.w(TAG, "Invalid transform (" + s + ")");
        }
        return matrix;
    }


    private static float angle(float x1, float y1, float x2, float y2) {
        return (float) Math.toDegrees(Math.atan2(x1, y1) - Math.atan2(x2, y2)) % 360;
    }

    private static final RectF arcRectf = new RectF();
    private static final Matrix arcMatrix = new Matrix();
    private static final Matrix arcMatrix2 = new Matrix();

    private static void drawArc(Path p, float lastX, float lastY, float x, float y, float rx, float ry, float theta, int largeArc, int sweepArc) {
        // Log.d("drawArc", "from (" + lastX + "," + lastY + ") to (" + x + ","+
        // y + ") r=(" + rx + "," + ry +
        // ") theta=" + theta + " flags="+ largeArc + "," + sweepArc);

        // http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes

        if (rx == 0 || ry == 0) {
            p.lineTo(x, y);
            return;
        }

        if (x == lastX && y == lastY) {
            return; // nothing to draw
        }

        rx = Math.abs(rx);
        ry = Math.abs(ry);

        final float thrad = theta * (float) Math.PI / 180;
        final float st = FloatMath.sin(thrad);
        final float ct = FloatMath.cos(thrad);

        final float xc = (lastX - x) / 2;
        final float yc = (lastY - y) / 2;
        final float x1t = ct * xc + st * yc;
        final float y1t = -st * xc + ct * yc;

        final float x1ts = x1t * x1t;
        final float y1ts = y1t * y1t;
        float rxs = rx * rx;
        float rys = ry * ry;

        float lambda = (x1ts / rxs + y1ts / rys) * 1.001f; // add 0.1% to be
        // sure that no out
        // of range occurs
        // due to
        // limited precision
        if (lambda > 1) {
            float lambdasr = FloatMath.sqrt(lambda);
            rx *= lambdasr;
            ry *= lambdasr;
            rxs = rx * rx;
            rys = ry * ry;
        }

        final float R = FloatMath.sqrt((rxs * rys - rxs * y1ts - rys * x1ts) / (rxs * y1ts + rys * x1ts)) * ((largeArc == sweepArc) ? -1 : 1);
        final float cxt = R * rx * y1t / ry;
        final float cyt = -R * ry * x1t / rx;
        final float cx = ct * cxt - st * cyt + (lastX + x) / 2;
        final float cy = st * cxt + ct * cyt + (lastY + y) / 2;

        final float th1 = angle(1, 0, (x1t - cxt) / rx, (y1t - cyt) / ry);
        float dth = angle((x1t - cxt) / rx, (y1t - cyt) / ry, (-x1t - cxt) / rx, (-y1t - cyt) / ry);

        if (sweepArc == 0 && dth > 0) {
            dth -= 360;
        } else if (sweepArc != 0 && dth < 0) {
            dth += 360;
        }

        // draw
        if ((theta % 360) == 0) {
            // no rotate and translate need
            arcRectf.set(cx - rx, cy - ry, cx + rx, cy + ry);
            p.arcTo(arcRectf, th1, dth);
        } else {
            // this is the hard and slow part :-)
            arcRectf.set(-rx, -ry, rx, ry);

            arcMatrix.reset();
            arcMatrix.postRotate(theta);
            arcMatrix.postTranslate(cx, cy);
            arcMatrix.invert(arcMatrix2);

            p.transform(arcMatrix2);
            p.arcTo(arcRectf, th1, dth);
            p.transform(arcMatrix);
        }
    }

    private static NumberParse getNumberParseAttr(String name, Attributes attributes) {
        int n = attributes.getLength();
        for (int i = 0; i < n; i++) {
            if (attributes.getLocalName(i).equals(name)) {
                return parseNumbers(attributes.getValue(i));
            }
        }
        return null;
    }

    private static String getStringAttr(String name, Attributes attributes) {
        int n = attributes.getLength();
        for (int i = 0; i < n; i++) {
            if (attributes.getLocalName(i).equals(name)) {
                return attributes.getValue(i);
            }
        }
        return null;
    }

    private static Float getFloatAttr(String name, Attributes attributes) {
        return getFloatAttr(name, attributes, null);
    }

    private static Float getFloatAttr(String name, Attributes attributes, Float defaultValue) {
        String v = getStringAttr(name, attributes);
        return parseFloatValue(v, defaultValue);
    }

    private static float getFloatAttr(String name, Attributes attributes, float defaultValue) {
        String v = getStringAttr(name, attributes);
        return parseFloatValue(v, defaultValue);
    }

    private static Float parseFloatValue(String str, Float defaultValue) {
        if (str == null) {
            return defaultValue;
        } else if (str.endsWith("px")) {
            str = str.substring(0, str.length() - 2);
        } else if (str.endsWith("%")) {
            str = str.substring(0, str.length() - 1);
            return Float.parseFloat(str) / 100;
        }
        // Log.d(TAG, "Float parsing '" + name + "=" + v + "'");
        return Float.parseFloat(str);
    }

    private static class NumberParse {
        private ArrayList<Float> numbers;
        private int nextCmd;

        public NumberParse(ArrayList<Float> numbers, int nextCmd) {
            this.numbers = numbers;
            this.nextCmd = nextCmd;
        }

        public int getNextCmd() {
            return nextCmd;
        }

        public float getNumber(int index) {
            return numbers.get(index);
        }

    }

    private static class Gradient {
        String id;
        String xlink;
        boolean isLinear;
        float x1, y1, x2, y2;
        float x, y, radius;
        ArrayList<Float> positions = new ArrayList<Float>();
        ArrayList<Integer> colors = new ArrayList<Integer>();
        Matrix matrix = null;
        public Shader shader = null;
        public boolean boundingBox = false;
        public TileMode tilemode;

        public Gradient createChild(Gradient g) {
            Gradient child = new Gradient();
            child.id = g.id;
            child.xlink = id;
            child.isLinear = g.isLinear;
            child.x1 = g.x1;
            child.x2 = g.x2;
            child.y1 = g.y1;
            child.y2 = g.y2;
            child.x = g.x;
            child.y = g.y;
            child.radius = g.radius;
            child.positions = positions;
            child.colors = colors;
            child.matrix = matrix;
            if (g.matrix != null) {
                if (matrix == null) {
                    child.matrix = g.matrix;
                } else {
                    Matrix m = new Matrix(matrix);
                    m.preConcat(g.matrix);
                    child.matrix = m;
                }
            }
            child.boundingBox = g.boundingBox;
            child.shader = g.shader;
            child.tilemode = g.tilemode;
            return child;
        }
    }

    private static class StyleSet {
        HashMap<String, String> styleMap = new HashMap<String, String>();

        private StyleSet(String string) {
            String[] styles = string.split(";");
            for (String s : styles) {
                String[] style = s.split(":");
                if (style.length == 2) {
                    styleMap.put(style[0], style[1]);
                }
            }
        }

        public String getStyle(String name) {
            return styleMap.get(name);
        }
    }

    private static class Properties {
        StyleSet styles = null;
        Attributes atts;

        private Properties(Attributes atts) {
            this.atts = atts;
            String styleAttr = getStringAttr("style", atts);
            if (styleAttr != null) {
                styles = new StyleSet(styleAttr);
            }
        }

        public String getAttr(String name) {
            String v = null;
            if (styles != null) {
                v = styles.getStyle(name);
            }
            if (v == null) {
                v = getStringAttr(name, atts);
            }
            return v;
        }

        public String getString(String name) {
            return getAttr(name);
        }

        private Integer rgb(int r, int g, int b) {
            return ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff);
        }

        private int parseNum(String v) throws NumberFormatException {
            if (v.endsWith("%")) {
                v = v.substring(0, v.length() - 1);
                return Math.round(Float.parseFloat(v) / 100 * 255);
            }
            return Integer.parseInt(v);
        }

        public Integer getColor(String name) {
            String v = getAttr(name);
            if (v == null) {
                return null;
            } else if (v.startsWith("#")) {
                try {
                    int c = Integer.parseInt(v.substring(1), 16);
                    return v.length() == 4 ? hex3Tohex6(c) : c;
                } catch (NumberFormatException nfe) {
                    return null;
                }
            } else if (v.startsWith("rgb(") && v.endsWith(")")) {
                String values[] = v.substring(4, v.length() - 1).split(",");
                try {
                    return rgb(parseNum(values[0]), parseNum(values[1]), parseNum(values[2]));
                } catch (NumberFormatException nfe) {
                    return null;
                } catch (ArrayIndexOutOfBoundsException e) {
                    return null;
                }
            } else {
                return SVGColors.mapColour(v);
            }
        }

        // convert 0xRGB into 0xRRGGBB
        private int hex3Tohex6(int x) {
            return (x & 0xF00) << 8 | (x & 0xF00) << 12 | (x & 0xF0) << 4 | (x & 0xF0) << 8 | (x & 0xF) << 4 | (x & 0xF);
        }

        public float getFloat(String name, float defaultValue) {
            String v = getAttr(name);
            if (v == null) {
                return defaultValue;
            } else {
                try {
                    return Float.parseFloat(v);
                } catch (NumberFormatException nfe) {
                    return defaultValue;
                }
            }
        }

        public Float getFloat(String name, Float defaultValue) {
            String v = getAttr(name);
            if (v == null) {
                return defaultValue;
            } else {
                try {
                    return Float.parseFloat(v);
                } catch (NumberFormatException nfe) {
                    return defaultValue;
                }
            }
        }

        public Float getFloat(String name) {
            return getFloat(name, null);
        }
    }

    private static class LayerAttributes {
        public final float opacity;

        public LayerAttributes(float opacity) {
            this.opacity = opacity;
        }
    }

    static class SVGHandler extends DefaultHandler {

        private Picture picture;
        private Canvas canvas;
        private Float limitsAdjustmentX, limitsAdjustmentY;

        final LinkedList<LayerAttributes> layerAttributeStack = new LinkedList<LayerAttributes>();

        Paint strokePaint;
        boolean strokeSet = false;
        final LinkedList<Paint> strokePaintStack = new LinkedList<Paint>();
        final LinkedList<Boolean> strokeSetStack = new LinkedList<Boolean>();

        Paint fillPaint;
        boolean fillSet = false;
        final LinkedList<Paint> fillPaintStack = new LinkedList<Paint>();
        final LinkedList<Boolean> fillSetStack = new LinkedList<Boolean>();

        // Scratch rect (so we aren't constantly making new ones)
        final RectF rect = new RectF();
        RectF bounds = null;
        final RectF limits = new RectF(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);

        Integer searchColor = null;
        Integer replaceColor = null;
        Float opacityMultiplier = null;

        boolean whiteMode = false;

        Integer canvasRestoreCount;

        final LinkedList<Boolean> transformStack = new LinkedList<Boolean>();
        final LinkedList<Matrix> matrixStack = new LinkedList<Matrix>();

        final HashMap<String, Gradient> gradientMap = new HashMap<String, Gradient>();
        Gradient gradient = null;
        // text
        private String preTag;
        private float text_x;
        private String text_color;
        private String text_id;
        private float text_y;
        private String text_size;
        float[] text_retate;

        //
        private static final String FLOOR = "floor";
        private static final String ID = "id";
        private static final String LOCATION = "location";
        private static final String NORTH = "north";

        //
        private static final String SHAPE = "shape";
        private static final String TYPE = "type";
        private static final String NAME = "name";
        private static final String URL = "url";
        private static final String CENTER_X = "center-x";
        private static final String CENTER_Y = "center-y";
        private static final String FILL_COLOR = "fill";
        private static final String HEIGHT = "height";
        private static final String WIDTH = "width";
        private Context context;

        public FloorsFeature getFloorsFeature() {
            return floorsFeature;
        }

        private FloorsFeature floorsFeature;
        private Feature feature;
        private List<Map<Long, Feature>> features;
        private List<Node> nodes = null;
        private long feature_id;

        public SVGHandler(Context context) {
            this.context = context;
            strokePaint = new Paint();
            strokePaint.setAntiAlias(true);
            strokePaint.setStyle(Paint.Style.STROKE);
            fillPaint = new Paint();
            fillPaint.setAntiAlias(true);
            fillPaint.setStyle(Paint.Style.FILL);
            matrixStack.addFirst(new Matrix());
            layerAttributeStack.addFirst(new LayerAttributes(1f));
        }

        void setPicture(Picture picture) {
            this.picture = picture;
        }

//        public void setColorSwap(Integer searchColor, Integer replaceColor, boolean overideOpacity) {
//            this.searchColor = searchColor;
//            this.replaceColor = replaceColor;
//            if (replaceColor != null && overideOpacity) {
//                opacityMultiplier = ((replaceColor >> 24) & 0x000000FF) / 255f;
//            } else {
//                opacityMultiplier = null;
//            }
//        }

//        public void setWhiteMode(boolean whiteMode) {
//            this.whiteMode = whiteMode;
//        }

        @Override
        public void startDocument() throws SAXException {
            // Set up prior to parsing a doc
            Log.d(TAG, "startDocument");
            floorsFeature = new FloorsFeature();
            strokeNodes = new ArrayList<>();
            strokeNodeList = new ArrayList<>();
        }

        @Override
        public void endDocument() throws SAXException {
            // Clean up after parsing a doc
            Log.d(TAG, "endDocument");

            floorsFeature.setStrokeNodes(strokeNodeList);
            // 话 logo

//            for (int i = 0; i < floorsFeature.getFeatures().size(); i++) {
//                Map<Long, Feature> map = floorsFeature.getFeatures().get(i);
//
//                for (Map.Entry<Long, Feature> entry : map.entrySet()) {
//                    String spaceTypeString = entry.getValue().getSpaceTypeString();
//
//                    float center_x = entry.getValue().getCenter_x();
//                    float center_y = entry.getValue().getCenter_y();
//                    Bitmap bitmap = null;
//                    if (!TextUtils.isEmpty(spaceTypeString) && TextUtils.equals(spaceTypeString, "01")) {
//                        // 会议室
////                        bitmap = BitmapUtils.small(context.getAssets(), "image/icon_meetingroom.png");
//                        bitmap = BitmapUtils.decodeSampleBitmapFromAssets(context.getAssets(), "image/icon_meetingroom.png");
//                        canvas.save();
//                        canvas.drawBitmap(bitmap, center_x, center_y, new Paint());
//                        canvas.restore();
//
//                    } else if (!TextUtils.isEmpty(spaceTypeString) && TextUtils.equals(spaceTypeString, "02")) {
////                        bitmap = BitmapUtils.small(context.getAssets(), "image/icon_toilet.png");
//                        bitmap = BitmapUtils.decodeSampleBitmapFromAssets(context.getAssets(), "image/icon_toilet.png");
//                        canvas.drawBitmap(bitmap, center_x, center_y, new Paint());
//                    } else if (!TextUtils.isEmpty(spaceTypeString) && TextUtils.equals(spaceTypeString, "04")) {
////                        bitmap = BitmapUtils.small(context.getAssets(), "image/icon_elevator.png");
//                        bitmap = BitmapUtils.decodeSampleBitmapFromAssets(context.getAssets(), "image/icon_elevator.png");
//                        canvas.drawBitmap(bitmap, center_x, center_y, new Paint());
//                    } else if (!TextUtils.isEmpty(spaceTypeString) && TextUtils.equals(spaceTypeString, "05")) {
////                        bitmap = BitmapUtils.small(context.getAssets(), "image/icon_stairs.png");
//                        bitmap = BitmapUtils.decodeSampleBitmapFromAssets(context.getAssets(), "image/icon_stairs.png");
//                        canvas.drawBitmap(bitmap, center_x, center_y, new Paint());
//                    } else if (!TextUtils.isEmpty(spaceTypeString) && TextUtils.equals(spaceTypeString, "07")) {
////                        bitmap = BitmapUtils.small(context.getAssets(), "image/icon_parking.png");
//                        bitmap = BitmapUtils.decodeSampleBitmapFromAssets(context.getAssets(), "image/icon_parking.png");
//                        canvas.drawBitmap(bitmap, center_x, center_y, new Paint());
//                    } else if (!TextUtils.isEmpty(spaceTypeString) && TextUtils.equals(spaceTypeString, "17")) {
////                        bitmap = BitmapUtils.small(context.getAssets(), "image/icon_diningroom.png");
//                        bitmap = BitmapUtils.decodeSampleBitmapFromAssets(context.getAssets(), "image/icon_diningroom.png");
//                        canvas.drawBitmap(bitmap, center_x, center_y, new Paint());
//                    }
//
//                }
//            }
        }

        private final Matrix gradMatrix = new Matrix();

        private boolean doFill(Properties atts, RectF bounding_box) {
            if ("none".equals(atts.getString("display"))) {
                return false;
            }
//            if (whiteMode) {
//                fillPaint.setShader(null);
//                fillPaint.setColor(Color.WHITE);
//                return true;
//            }
            String fillString = atts.getString("fill");
            if (fillString != null) {

                if (fillString.startsWith("url(#")) {

                    // It's a gradient fill, look it up in our map
                    String id = fillString.substring("url(#".length(), fillString.length() - 1);
                    Gradient g = gradientMap.get(id);
                    Shader shader = null;
                    if (g != null) {
                        shader = g.shader;
                    }
                    if (shader != null) {
                        // Util.debug("Found shader!");
                        fillPaint.setShader(shader);
                        gradMatrix.set(g.matrix);
                        if (g.boundingBox && bounding_box != null) {
                            // Log.d("svg", "gradient is bounding box");
                            gradMatrix.preTranslate(bounding_box.left, bounding_box.top);
                            gradMatrix.preScale(bounding_box.width(), bounding_box.height());
                        }
                        shader.setLocalMatrix(gradMatrix);
                        return true;
                    } else {
                        Log.w(TAG, "Didn't find shader, using black: " + id);
                        fillPaint.setShader(null);
                        doColor(atts, Color.BLACK, true, fillPaint);
                        return true;
                    }
                } else if (fillString.equalsIgnoreCase("none")) {
                    fillPaint.setShader(null);
                    fillPaint.setColor(Color.TRANSPARENT);
                    return true;
                } else {
                    fillPaint.setShader(null);
                    Integer color = atts.getColor("fill");
                    if (color != null) {
                        doColor(atts, color, true, fillPaint);
                        return true;
                    } else {
                        Log.w(TAG, "Unrecognized fill color, using black: " + fillString);
                        doColor(atts, Color.BLACK, true, fillPaint);
                        return true;
                    }
                }
            } else {
                if (shape_id == 0 && type_id == 0) {
                    fillPaint.setShader(null);
                    fillPaint.setColor(Color.RED);
                }
                return true;
            }
        }

        private boolean doStroke(Properties atts) {
//            if (whiteMode) {
//                // Never stroke in white mode
//                return false;
//            }
            if ("none".equals(atts.getString("display"))) {
                return false;
            }

            // Check for other stroke attributes
            Float width = atts.getFloat("stroke-width");
            if (width != null) {
                strokePaint.setStrokeWidth(width);
            }

            String linecap = atts.getString("stroke-linecap");
            if ("round".equals(linecap)) {
                strokePaint.setStrokeCap(Paint.Cap.ROUND);
            } else if ("square".equals(linecap)) {
                strokePaint.setStrokeCap(Paint.Cap.SQUARE);
            } else if ("butt".equals(linecap)) {
                strokePaint.setStrokeCap(Paint.Cap.BUTT);
            }

            String linejoin = atts.getString("stroke-linejoin");
            if ("miter".equals(linejoin)) {
                strokePaint.setStrokeJoin(Paint.Join.MITER);
            } else if ("round".equals(linejoin)) {
                strokePaint.setStrokeJoin(Paint.Join.ROUND);
            } else if ("bevel".equals(linejoin)) {
                strokePaint.setStrokeJoin(Paint.Join.BEVEL);
            }

            pathStyleHelper(atts.getString("stroke-dasharray"), atts.getString("stroke-dashoffset"));

            String strokeString = atts.getAttr("stroke");
            if (strokeString != null) {
                if (strokeString.equalsIgnoreCase("none")) {
                    strokePaint.setColor(Color.TRANSPARENT);
                    return false;
                } else {
                    Integer color = atts.getColor("stroke");
                    if (color != null) {
                        doColor(atts, color, false, strokePaint);
                        return true;
                    } else {
                        Log.w(TAG, "Unrecognized stroke color, using none: " + strokeString);
                        strokePaint.setColor(Color.TRANSPARENT);
                        return false;
                    }
                }
            } else {
                if (strokeSet) {
                    // Inherit from parent
                    return strokePaint.getColor() != Color.TRANSPARENT; // optimization
                } else {
                    // Default is none
                    strokePaint.setColor(Color.TRANSPARENT);
                    return false;
                }
            }
        }

//        private Gradient doGradient(boolean isLinear, Attributes atts) {
//            Gradient gradient = new Gradient();
//            gradient.id = getStringAttr("id", atts);
//            gradient.isLinear = isLinear;
//            if (isLinear) {
//                gradient.x1 = getFloatAttr("x1", atts, 0f);
//                gradient.x2 = getFloatAttr("x2", atts, 1f);
//                gradient.y1 = getFloatAttr("y1", atts, 0f);
//                gradient.y2 = getFloatAttr("y2", atts, 0f);
//            } else {
//                gradient.x = getFloatAttr("cx", atts, 0f);
//                gradient.y = getFloatAttr("cy", atts, 0f);
//                gradient.radius = getFloatAttr("r", atts, 0f);
//            }
//            String transform = getStringAttr("gradientTransform", atts);
//            if (transform != null) {
//                gradient.matrix = parseTransform(transform);
//            }
//            String spreadMethod = getStringAttr("spreadMethod", atts);
//            if (spreadMethod == null) {
//                spreadMethod = "pad";
//            }
//
//            gradient.tilemode = (spreadMethod.equals("reflect")) ? TileMode.MIRROR : (spreadMethod.equals("repeat")) ? TileMode.REPEAT : TileMode.CLAMP;
//
//            String unit = getStringAttr("gradientUnits", atts);
//            if (unit == null) {
//                unit = "objectBoundingBox";
//            }
//            gradient.boundingBox = !unit.equals("userSpaceOnUse");
//
//            String xlink = getStringAttr("href", atts);
//            if (xlink != null) {
//                if (xlink.startsWith("#")) {
//                    xlink = xlink.substring(1);
//                }
//                gradient.xlink = xlink;
//            }
//            return gradient;
//        }


        private float maxX = 0;
        private float maxY = 0;
        private float minX = Integer.MAX_VALUE;
        private float minY = Integer.MAX_VALUE;

        private float area_maxX = 0;
        private float area_maxY = 0;
        private float area_minX = Integer.MAX_VALUE;
        private float area_minY = Integer.MAX_VALUE;

        private Node node_first;

        /**
         * This is where the hard-to-parse paths are handled. Uppercase rules are
         * absolute positions, lowercase are relative. Types of path rules:
         * <p>
         * <ol>
         * <li>M/m - (x y)+ - Move to (without drawing)
         * <li>Z/z - (no params) - Close path (back to starting point)
         * <li>L/l - (x y)+ - Line to
         * <li>H/h - x+ - Horizontal ine to
         * <li>V/v - y+ - Vertical line to
         * <li>C/c - (x1 y1 x2 y2 x y)+ - Cubic bezier to
         * <li>S/s - (x2 y2 x y)+ - Smooth cubic bezier to (shorthand that assumes
         * the x2, y2 from previous C/S is the x1, y1 of this bezier)
         * <li>Q/q - (x1 y1 x y)+ - Quadratic bezier to
         * <li>T/t - (x y)+ - Smooth quadratic bezier to (assumes previous control
         * point is "reflection" of last one w.r.t. to current point)
         * </ol>
         * <p>
         * Numbers are separate by whitespace, comma or nothing at all (!) if they
         * are self-delimiting, (ie. begin with a - sign)
         * M = moveto
         * L = lineto
         * H = horizontal lineto
         * V = vertical lineto
         * C = curveto
         * S = smooth curveto
         * Q = quadratic Belzier curve
         * T = smooth quadratic Belzier curveto
         * A = elliptical Arc
         * Z = closepath
         * ***************
         * <path d="M153 334
         * C153 334 151 334 151 334
         * C151 339 153 344 156 344
         * C164 344 171 339 171 334
         * C171 322 164 314 156 314
         * C142 314 131 322 131 334
         * C131 350 142 364 156 364
         * C175 364 191 350 191 334
         * C191 311 175 294 156 294
         * C131 294 111 311 111 334
         * C111 361 131 384 156 384
         * C186 384 211 361 211 334
         * C211 300 186 274 156 274"
         * style="fill:white;stroke:red;stroke-width:2"/>
         *
         * @param s the path string from the XML
         */
        private Path doPath(String s) {
            int n = s.length();
            ParserHelper ph = new ParserHelper(s, 0);
            ph.skipWhitespace();
            Path path = new Path();
            float lastX = 0;
            float lastY = 0;
            float lastX1 = 0;
            float lastY1 = 0;
            float subPathStartX = 0;
            float subPathStartY = 0;
            char prevCmd = 0;
            while (ph.pos < n) {
                char cmd = s.charAt(ph.pos);
                switch (cmd) {
                    case '-':
                    case '+':
                    case '0':
                    case '1':
                    case '2':
                    case '3':
                    case '4':
                    case '5':
                    case '6':
                    case '7':
                    case '8':
                    case '9':
                        if (prevCmd == 'm' || prevCmd == 'M') {
                            cmd = (char) ((prevCmd) - 1);
                            break;
                        } else if (("lhvcsqta").indexOf(Character.toLowerCase(prevCmd)) >= 0) {
                            cmd = prevCmd;
                            break;
                        }
                    default: {
                        ph.advance();
                        prevCmd = cmd;
                    }
                }

                boolean wasCurve = false;
                switch (cmd) {
                    case 'M':
                    case 'm': {
                        float x = ph.nextFloat();
                        float y = ph.nextFloat();
                        nodes.clear();
                        if (cmd == 'm') {
                            subPathStartX += x;
                            subPathStartY += y;
                            path.rMoveTo(x, y);
//                            path.rMoveTo(x, (float) (-y + canvas_height));
                            node_first = new Node(x, y);
                            lastX += x;
                            lastY += y;
                        } else {
                            subPathStartX = x;
                            subPathStartY = -y;
                            path.moveTo(x, y);
//                            nodes.add(new Node(x, -y));
                            node_first = new Node(x, y);
                            lastX = x;
                            lastY = y;
                        }

                        area_maxX = 0;
                        area_maxY = 0;
                        area_minX = Integer.MAX_VALUE;
                        area_minY = Integer.MAX_VALUE;
                        break;
                    }
                    case 'Z':
                    case 'z': {
                        path.close();
                        path.moveTo(subPathStartX, subPathStartY);
//                        nodes.add(new Node(subPathStartX, subPathStartY));
                        lastX = subPathStartX;
                        lastY = subPathStartY;
                        lastX1 = subPathStartX;
                        lastY1 = subPathStartY;
                        wasCurve = true;

                        if (nodes.size() >= 1) {
                            nodes.add(node_first);
                        }
                        break;
                    }
                    case 'T':
                    case 't':
                        // todo - smooth quadratic Bezier (two parameters)
                    case 'L':
                    case 'l': {
                        float x = ph.nextFloat();
                        float y = ph.nextFloat();
                        if (cmd == 'l') {
                            path.lineTo(x, y);
                            nodes.add(new Node(x, y));


                            if (x >= maxX) {
                                maxX = x;
                            }

                            if (y >= maxY) {
                                maxY = y;
                            }

                            if (x <= minX) {
                                minX = x;
                            }

                            if (y <= minY) {
                                minY = y;
                            }

                            ///
                            if (x >= area_maxX) {
                                area_maxX = x;
                            }

                            if (y >= area_maxY) {
                                area_maxY = y;
                            }
                            ///
                            if (x <= area_minX) {
                                area_minX = x;
                            }

                            if (y <= area_minY) {
                                area_minY = y;
                            }

                            lastX += x;
                            lastY += y;
                        } else {
                            path.lineTo(x, y);
                            nodes.add(new Node(x, y));
                            lastX = x;
                            lastY = y;
                        }
                        System.out.print("");
                        break;
                    }
                    case 'H':
                    case 'h': {
                        float x = ph.nextFloat();
                        if (cmd == 'h') {
                            path.rLineTo(x, 0);
//                            nodes.add(new Node(x, 0));
                            lastX += x;
                        } else {
                            path.lineTo(x, lastY);
//                            nodes.add(new Node(x, lastY));
                            lastX = x;
                        }
                        break;
                    }
                    case 'V':
                    case 'v': {
                        float y = ph.nextFloat();
                        if (cmd == 'v') {
                            path.rLineTo(0, y);
//                            nodes.add(new Node(0, y));
                            lastY += y;
                        } else {
                            path.lineTo(lastX, y);
//                            nodes.add(new Node(lastX, y));
                            lastY = y;
                        }
                        break;
                    }
                    case 'C':
                    case 'c': {
                        wasCurve = true;
                        float x1 = ph.nextFloat();
                        float y1 = ph.nextFloat();
                        float x2 = ph.nextFloat();
                        float y2 = ph.nextFloat();
                        float x = ph.nextFloat();
                        float y = ph.nextFloat();
                        if (cmd == 'c') {
                            x1 += lastX;
                            x2 += lastX;
                            x += lastX;
                            y1 += lastY;
                            y2 += lastY;
                            y += lastY;
                        }
                        path.cubicTo(x1, y1, x2, y2, x, y);
//                        nodes.add(new Node())
                        // 暂不支持贝塞尔曲线
//                        Log.d("cubicTo", "x1 == " + x1 + " y1 == " + y1 + " x2 == " + x2 + " y2 == " + y2 + " x == " + x + " y == " + y);
                        lastX1 = x2;
                        lastY1 = y2;
                        lastX = x;
                        lastY = y;
                        break;
                    }
                    case 'Q':
                    case 'q':
                        // todo - quadratic Bezier (four parameters)
                    case 'S':
                    case 's': {
                        wasCurve = true;
                        float x2 = ph.nextFloat();
                        float y2 = ph.nextFloat();
                        float x = ph.nextFloat();
                        float y = ph.nextFloat();
                        if (Character.isLowerCase(cmd)) {
                            x2 += lastX;
                            x += lastX;
                            y2 += lastY;
                            y += lastY;
                        }
                        float x1 = 2 * lastX - lastX1;
                        float y1 = 2 * lastY - lastY1;
                        path.cubicTo(x1, y1, x2, y2, x, y);
                        //暂不支持贝塞尔曲线
                        lastX1 = x2;
                        lastY1 = y2;
                        lastX = x;
                        lastY = y;
                        break;
                    }
                    case 'A':
                    case 'a': {
                        float rx = ph.nextFloat();
                        float ry = ph.nextFloat();
                        float theta = ph.nextFloat();
                        int largeArc = ph.nextFlag();
                        int sweepArc = ph.nextFlag();
                        float x = ph.nextFloat();
                        float y = ph.nextFloat();
                        if (cmd == 'a') {
                            x += lastX;
                            y += lastY;
                        }
                        drawArc(path, lastX, lastY, x, y, rx, ry, theta, largeArc, sweepArc);
                        lastX = x;
                        lastY = y;
                        break;
                    }
                    default:
                        Log.w(TAG, "Invalid path command: " + cmd);
                        ph.advance();
                }
                if (!wasCurve) {
                    lastX1 = lastX;
                    lastY1 = lastY;
                }
                ph.skipWhitespace();
            }
            return path;
        }


        private void doColor(Properties atts, Integer color, boolean fillMode, Paint paint) {

            int c = (0xFFFFFF & color) | 0xFF000000;
            if (searchColor != null && searchColor.intValue() == c) {
                c = replaceColor;
            }
            paint.setShader(null);
            paint.setColor(c);
            Float opacityAttr = atts.getFloat("opacity");
            if (opacityAttr == null) {
                opacityAttr = atts.getFloat(fillMode ? "fill-opacity" : "stroke-opacity");
            }

            float opacity = opacityAttr != null ? opacityAttr : 1f;
            opacity *= currentLayerAttributes().opacity;
            if (opacityMultiplier != null) {
                opacity *= opacityMultiplier;
            }
            paint.setAlpha((int) (255f * opacity));
        }


        /**
         * set the path style (if any) stroke-dasharray="n1,n2,..."
         * stroke-dashoffset=n
         */
        private void pathStyleHelper(String style, String offset) {
            if (style == null) {
                return;
            }

            if (style.equals("none")) {
                strokePaint.setPathEffect(null);
                return;
            }

            StringTokenizer st = new StringTokenizer(style, " ,");
            int count = st.countTokens();
            float[] intervals = new float[(count & 1) == 1 ? count * 2 : count];
            float max = 0;
            float current = 1f;
            int i = 0;
            while (st.hasMoreTokens()) {
                intervals[i++] = current = toFloat(st.nextToken(), current);
                max += current;
            }

            // in svg speak, we double the intervals on an odd count
            for (int start = 0; i < intervals.length; i++, start++) {
                max += intervals[i] = intervals[start];
            }

            float off = 0f;
            if (offset != null) {
                try {
                    off = Float.parseFloat(offset) % max;
                } catch (NumberFormatException e) {
                    // ignore
                }
            }

            strokePaint.setPathEffect(new DashPathEffect(intervals, off));
        }

        private static float toFloat(String s, float dflt) {
            float result = dflt;
            try {
                result = Float.parseFloat(s);
            } catch (NumberFormatException e) {
                // ignore
            }
            return result;
        }

        private boolean hidden = false;
        private int hiddenLevel = 0;
        private boolean boundsMode = false;

        private List<Node> strokeNodes;
        private List<List<Node>> strokeNodeList;

        private boolean isFirstParserNode = true;

        private void doLimits2(float x, float y) {
            if (x < limits.left) {
                limits.left = x;
            }
            if (x > limits.right) {
                limits.right = x;
            }
            if (y < limits.top) {
                limits.top = y;
            }
            if (y > limits.bottom) {
                limits.bottom = y;
            }
        }

        private final RectF tmpLimitRect = new RectF();

        private void doLimits(RectF box, Paint paint) {
            Matrix m = matrixStack.getLast();
            m.mapRect(tmpLimitRect, box);
            float width2 = (paint == null) ? 0 : paint.getStrokeWidth() / 2;
            doLimits2(tmpLimitRect.left - width2, tmpLimitRect.top - width2);
            doLimits2(tmpLimitRect.right + width2, tmpLimitRect.bottom + width2);
        }

        private void doLimits(RectF box) {
            doLimits(box, null);
        }

        private void pushTransform(Attributes atts) {
            final String transform = getStringAttr("transform", atts);
            boolean pushed = transform != null;
            transformStack.addLast(pushed);
            if (pushed) {
                final Matrix matrix = parseTransform(transform);
                canvas.save();
                canvas.concat(matrix);
                matrix.postConcat(matrixStack.getLast());
                matrixStack.addLast(matrix);
            }

        }

        private void popTransform() {
            if (transformStack.removeLast()) {
                canvas.restore();
                matrixStack.removeLast();
            }
        }

        private float canvas_width;
        private float canvas_height;
        private int shape_id;
        private int type_id;
        private String arer_name;

        @Override
        public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
            // Reset paint opacity
            strokePaint.setAlpha(255);
            fillPaint.setAlpha(255);
            // Ignore everything but rectangles in bounds mode
            if (boundsMode) {
                if (localName.equals("rect")) {
                    Float x = getFloatAttr("x", atts);
                    if (x == null) {
                        x = 0f;
                    }
                    Float y = getFloatAttr("y", atts);
                    if (y == null) {
                        y = 0f;
                    }
                    Float width = getFloatAttr("width", atts);
                    Float height = getFloatAttr("height", atts);
                    bounds = new RectF(x, y, x + width, y + height);
                }
                return;
            }


            if (localName.equals("svg")) {
                // 解析 svg 标签，一个SVG标签标示一个SVG图像
                features = new ArrayList<>();
                floorsFeature.setFloor_id(Long.parseLong(atts.getValue(ID)));
                floorsFeature.setFloor(atts.getValue(FLOOR));
                floorsFeature.setLocation(atts.getValue(LOCATION));
                floorsFeature.setNorth(Float.parseFloat(atts.getValue(NORTH)));
                floorsFeature.setHeight(Float.parseFloat(atts.getValue(HEIGHT)));
                floorsFeature.setWidth(Float.parseFloat(atts.getValue(WIDTH)));
                floorsFeature.setOffset_x(Float.parseFloat(atts.getValue("offset-x") == null ? "0" : atts.getValue("offset-x")));
                floorsFeature.setOffset_y(Float.parseFloat(atts.getValue("offset-y") == null ? "0" : atts.getValue("offset-y")));

                canvas = null;
                // 获取 viewBox(视区盒子)
                String viewboxStr = getStringAttr("viewBox", atts);
                if (viewboxStr != null) {
                    String[] dims = viewboxStr.replace(',', ' ').split("\\s+");
                    if (dims.length == 4) {
                        Float x1 = parseFloatValue(dims[0], null);
                        Float y1 = parseFloatValue(dims[1], null);
                        Float x2 = parseFloatValue(dims[2], null);
                        Float y2 = parseFloatValue(dims[3], null);

                        if (x1 != null && x2 != null && y1 != null && y2 != null) {
                            float width = FloatMath.ceil(x2 - x1);
                            float height = FloatMath.ceil(y2 - y1);
                            canvas = picture.beginRecording((int) width, (int) height);
                            canvasRestoreCount = canvas.save();
                            canvas.clipRect(0f, 0f, width, height);
                            limitsAdjustmentX = -x1;
                            limitsAdjustmentY = -y1;
                            canvas.translate(limitsAdjustmentX, limitsAdjustmentY);
                        }
                    }
                }
                // No viewbox
                if (canvas == null) {
                    canvas_width = (int) FloatMath.ceil(getFloatAttr("width", atts));
                    canvas_height = (int) FloatMath.ceil(getFloatAttr("height", atts));
                    canvas = picture.beginRecording((int) canvas_width, (int) canvas_height);
                    canvasRestoreCount = null;
                }


                canvas.drawColor(Color.parseColor("#F6F3EE"));

//                /////用于绘制坐标线
//                Paint paint = new Paint();
//                paint.setColor(Color.RED);
//                canvas.drawLine(0f, 0f, Byte.MAX_VALUE, 0f, paint);
//                canvas.drawLine(0f, 0f, 0f, Byte.MAX_VALUE, paint);

                ////////
            } else if (localName.equals("g")) {
                final Properties props = new Properties(atts);

                // Check to see if this is the "bounds" layer
                if ("bounds".equalsIgnoreCase(getStringAttr("id", atts))) {
                    boundsMode = true;
                }
                if (hidden) {
                    hiddenLevel++;
                    // Util.debug("Hidden up: " + hiddenLevel);
                }
                // Go in to hidden mode if display is "none"
                if ("none".equals(getStringAttr("display", atts)) || "none".equals(props.getString("display"))) {
                    if (!hidden) {
                        hidden = true;
                        hiddenLevel = 1;
                        // Util.debug("Hidden up: " + hiddenLevel);
                    }
                }

                // Create layer attributes
                final float opacity = props.getFloat("opacity", 1f);
                LayerAttributes curLayerAttr = currentLayerAttributes();
                LayerAttributes newLayerAttr = new LayerAttributes(curLayerAttr.opacity * opacity);
                layerAttributeStack.addLast(newLayerAttr);

                pushTransform(atts);
                fillPaintStack.addLast(new Paint(fillPaint));
                strokePaintStack.addLast(new Paint(strokePaint));
                fillSetStack.addLast(fillSet);
                strokeSetStack.addLast(strokeSet);

                doFill(props, null); // Added by mrn but a boundingBox is now
                // required by josef.
                doStroke(props);

                fillSet |= (props.getString("fill") != null);
                strokeSet |= (props.getString("stroke") != null);

            } else if (!hidden && localName.equals("rect")) {
                Float x = getFloatAttr("x", atts);
                if (x == null) {
                    x = 0f;
                }
                Float y = getFloatAttr("y", atts);
                if (y == null) {
                    y = 0f;
                }
                Float width = getFloatAttr("width", atts);
                Float height = getFloatAttr("height", atts);
                Float rx = getFloatAttr("rx", atts, 0f);
                Float ry = getFloatAttr("ry", atts, 0f);
                pushTransform(atts);
                Properties props = new Properties(atts);
                rect.set(x, y, x + width, y + height);
                if (doFill(props, rect)) {
                    rect.set(x, y, x + width, y + height);
                    if (rx <= 0f && ry <= 0f) {
                        canvas.drawRect(rect, fillPaint);
                    } else {
                        canvas.drawRoundRect(rect, rx, ry, fillPaint);
                    }
                    doLimits(rect);
                }
                if (doStroke(props)) {
                    rect.set(x, y, x + width, y + height);

                    if (rx <= 0f && ry <= 0f) {
                        canvas.drawRect(rect, strokePaint);
                    } else {
                        canvas.drawRoundRect(rect, rx, ry, strokePaint);
                    }
                    doLimits(rect, strokePaint);
                }
                popTransform();
            } else if (!hidden && localName.equals("path")) {

                isFirstParserNode = true;
                nodes = new ArrayList<>();
                feature = new Feature();
                feature_id = Long.parseLong(atts.getValue(ID) == null ? "-1" : atts.getValue(ID));
                feature.setFeature_id(feature_id);
                shape_id = Integer.parseInt(atts.getValue(SHAPE) == null ? "-1" : atts.getValue(SHAPE));

                type_id = Integer.parseInt(atts.getValue(TYPE) == null ? "-1" : atts.getValue(TYPE));
                feature.setShape_id(shape_id);
                feature.setType_id(type_id);

                arer_name = atts.getValue(NAME) == null ? "" : atts.getValue(NAME);

                feature.setUrl(atts.getValue(URL) == null ? "" : atts.getValue(URL));
                float center_x = Float.parseFloat(atts.getValue(CENTER_X));
                feature.setCenter_x(center_x);
                float center_y = Float.parseFloat(atts.getValue(CENTER_Y));
                feature.setCenter_y(center_y);
                //feature.setFillColor(Color.parseColor(atts.getValue("fill") == null ? "#000000" : atts.getValue("fill")));
                feature.setFillRule(atts.getValue("fill-rule") == null ? "" : atts.getValue("fill-rule"));
//                feature.setStrokeColor(Color.parseColor(atts.getValue("stroke") == null ? "#000000" : atts.getValue("stroke")));
                feature.setStrokeWidth(Float.parseFloat(atts.getValue("stroke-width") == null ? "0" : atts.getValue("stroke-width")));
                feature.setStrokeMiterLimit(Float.parseFloat(atts.getValue("stroke-miterlimit") == null ? "0" : atts.getValue("stroke-miterlimit")));

                int spaceType = Integer.parseInt(TextUtils.isEmpty(atts.getValue("spaceType")) ? "-1" : atts.getValue("spaceType"));
                String spaceTypeString = atts.getValue("spaceType");
                feature.setSpaceTypeString(spaceTypeString);
                feature.setSpaceType(spaceType);


//                Log.d("spaceType", "##   " + spaceType);

//                if (arer_name.equalsIgnoreCase("elevator")) {
//                    try {
//                        Bitmap bitmap1 = BitmapFactory.decodeStream(context.getAssets().open("image/icon_meetingroom.png"));
//                        canvas.drawBitmap(bitmap1, center_x, center_y, new Paint());
//                    } catch (IOException e) {
//                        e.printStackTrace();
//                    }
//                }
                ////////

//                if (shape_id == 2 && (type_id == 1 || type_id == 2)) {
//                    final String name = arer_name;
//                    Bitmap bitmap = null;
//                    if (name.equalsIgnoreCase("door")) {
//                        // 门口
//
//                        bitmap = BitmapUtils.decodeSampleBitmapFromAssets(context.getAssets(), "image/icon_door.png");
//                        arer_name = "";
////                        bitmap = BitmapUtils.decodeSampleBitmapFromAssets(context.getAssets(), "image/icon_door_meitu.png", 25, 25);
//                    } else if (name.equalsIgnoreCase("ask")) {
//                        // 服务台
////                        try {
////                            //   bitmap = BitmapFactory.decodeStream(context.getAssets().open("image/door.png"));
////                            Rect rect = new Rect();
////                            rect.inset((int) center_x, (int) center_y);
////                            BitmapFactory.Options options = new BitmapFactory.Options();
////                            options.outHeight = 15;
////                            options.outWidth = 15;
////                            bitmap = BitmapFactory.decodeStream(context.getAssets().open("image/icon_ask.png"), rect, options);
////                        } catch (IOException e) {
////                            e.printStackTrace();
////                        }
//                        bitmap = BitmapUtils.decodeSampleBitmapFromAssets(context.getAssets(), "image/icon_ask.png");
//                        arer_name = "";
////                        bitmap = BitmapUtils.decodeSampleBitmapFromAssets(context.getAssets(), "image/icon_ask_meitu.png", 15, 15);
//                    } else if (name.equalsIgnoreCase("elevator")) {
//                        // 升降梯
////                        try {
////                            //   bitmap = BitmapFactory.decodeStream(context.getAssets().open("image/door.png"));
////                            Rect rect = new Rect();
////                            rect.inset((int) center_x, (int) center_y);
////                            BitmapFactory.Options options = new BitmapFactory.Options();
////                            options.outHeight = 40;
////                            options.outWidth = 50;
////                            bitmap = BitmapFactory.decodeStream(context.getAssets().open("image/icon_elevator.png"), rect, options);
////                        } catch (IOException e) {
////                            e.printStackTrace();
////                        }
//                        bitmap = BitmapUtils.decodeSampleBitmapFromAssets(context.getAssets(), "image/icon_elevator.png");
//                        arer_name = "";
////                        bitmap = BitmapUtils.decodeSampleBitmapFromAssets(context.getAssets(), "image/icon_elevator_meitu.png", 40, 50);
//                    } else if (name.equalsIgnoreCase("escalator")) {
//                        // 扶梯
////                        try {
////                            //   bitmap = BitmapFactory.decodeStream(context.getAssets().open("image/door.png"));
////                            Rect rect = new Rect();
////                            rect.inset((int) center_x, (int) center_y);
////                            BitmapFactory.Options options = new BitmapFactory.Options();
////                            options.outHeight = 40;
////                            options.outWidth = 50;
////                            bitmap = BitmapFactory.decodeStream(context.getAssets().open("image/icon_escalator.png"), rect, options);
////                        } catch (IOException e) {
////                            e.printStackTrace();
////                        }
//                        bitmap = BitmapUtils.decodeSampleBitmapFromAssets(context.getAssets(), "image/icon_escalator.png");
//                        arer_name = "";
////                        bitmap = BitmapUtils.decodeSampleBitmapFromAssets(context.getAssets(), "image/icon_escalator_meitu.png", 40, 50);
//                    } else if (name.equalsIgnoreCase("exit")) {
//                        // 安全通道
////                        try {
////                            //   bitmap = BitmapFactory.decodeStream(context.getAssets().open("image/door.png"));
////                            Rect rect = new Rect();
////                            rect.inset((int) center_x, (int) center_y);
////                            BitmapFactory.Options options = new BitmapFactory.Options();
////                            options.outHeight = 24;
////                            options.outWidth = 24;
////                            bitmap = BitmapFactory.decodeStream(context.getAssets().open("image/door.png"), rect, options);
////                        } catch (IOException e) {
////                            e.printStackTrace();
////                        }
//                        bitmap = BitmapUtils.decodeSampleBitmapFromAssets(context.getAssets(), "image/icon_door.png");
//                        arer_name = "";
////                        bitmap = BitmapUtils.decodeSampleBitmapFromAssets(context.getAssets(), "image/icon_door_meitu.png", 24, 24);
//                    } else if (name.equalsIgnoreCase("stairs")) {
//                        // 楼梯
////                        try {
////                            //   bitmap = BitmapFactory.decodeStream(context.getAssets().open("image/door.png"));
////                            Rect rect = new Rect();
////                            rect.inset((int) center_x, (int) center_y);
////                            BitmapFactory.Options options = new BitmapFactory.Options();
////                            options.outHeight = 24;
////                            options.outWidth = 24;
////                            bitmap = BitmapFactory.decodeStream(context.getAssets().open("image/icon_stairs.png"), rect, options);
////                        } catch (IOException e) {
////                            e.printStackTrace();
////                        }
//                        bitmap = BitmapUtils.decodeSampleBitmapFromAssets(context.getAssets(), "image/icon_stairs.png");
//                        arer_name = "";
////                        bitmap = BitmapUtils.decodeSampleBitmapFromAssets(context.getAssets(), "image/icon_stairs_meitu.png", 24, 24);
//                    } else if (name.equalsIgnoreCase("toilet")) {
//                        // 洗手间
////                        try {
////                            //   bitmap = BitmapFactory.decodeStream(context.getAssets().open("image/door.png"));
////                            Rect rect = new Rect();
////                            rect.inset((int) center_x, (int) center_y);
////                            BitmapFactory.Options options = new BitmapFactory.Options();
////                            options.outHeight = 24;
////                            options.outWidth = 24;
////                            bitmap = BitmapFactory.decodeStream(context.getAssets().open("image/icon_toilet.png"), rect, options);
////                        } catch (IOException e) {
////                            e.printStackTrace();
////                        }
//
//
//                        bitmap = BitmapUtils.decodeSampleBitmapFromAssets(context.getAssets(), "image/icon_toilet.png");
//                        arer_name = "";
////                        bitmap = BitmapUtils.decodeSampleBitmapFromAssets(context.getAssets(), "image/icon_toilet_meitu.png", 24, 24);
//                    }
//
//                    try {
//                        canvas.drawBitmap(bitmap, center_x, center_y, new Paint());
//                    } catch (NullPointerException e) {
//                        Log.e("NullPointerException", e.getLocalizedMessage());
//                    }
//
//                }


                feature.setName(arer_name);

                /////////
                Path p = doPath(getStringAttr("d", atts));

                feature.setPath(p);

                pushTransform(atts);
                Properties props = new Properties(atts);
                p.computeBounds(rect, false);


                if (!(shape_id == 0 && type_id == 0)) {
                    if (doFill(props, rect)) {
                        canvas.drawPath(p, fillPaint);
                        doLimits(rect);
                    }
                }
                if (doStroke(props)) {
                    canvas.drawPath(p, strokePaint);
                    doLimits(rect, strokePaint);
                }
                popTransform();

            } else if (!hidden && localName.equals("text")) {


                text_x = getFloatAttr("x", atts);

                text_y = getFloatAttr("y", atts);


                text_size = getStringAttr("font-size", atts);

                text_color = getStringAttr("fill", atts);
                text_id = getStringAttr("id", atts);


                // 旋转矩阵
                String transform = getStringAttr("transform", atts);

//                transform.

                Log.d("transform", transform + "");

                if (transform.startsWith("rotate")) {
                    text_retate = getRotateParam(transform);
                } else {
                    text_retate = null;
                }
                preTag = localName;

            } else if (!hidden && localName.equals("image")) {
                String res = atts.getValue("img");
                float width = Float.parseFloat(atts.getValue("width"));
                float height = Float.parseFloat(atts.getValue("height"));
                float x = Float.parseFloat(atts.getValue("x"));
                float y = Float.parseFloat(atts.getValue("y"));

                try {
                    Bitmap bitmap = BitmapFactory.decodeStream(context.getAssets().open("image/ask.png"), null, null);
//                    bitmap.setDensity(240);
//                    canvas.setDensity(480);
//                    canvas.drawBitmap(bitmap, x, y, null);
                    canvas.drawBitmap(BitmapHelper.scaleBitmap(bitmap, width, height, false), x, y, null);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else if (!hidden) {
                Log.w(TAG, "UNRECOGNIZED SVG COMMAND: " + localName);
            }
        }

        public LayerAttributes currentLayerAttributes() {
            return layerAttributeStack.getLast();
        }

        @Override
        public void characters(char ch[], int start, int length) {

            // no-op
            if (!TextUtils.isEmpty(preTag) & TextUtils.equals(preTag, "text")) {
                String message = new String(ch, start, length);

                Paint paint = new Paint();
                paint.setColor(Color.parseColor(text_color));
                paint.setTextSize(Integer.parseInt(text_size));

                //  rotate(89.2625 1919.81 942.577) matrix(2.46244 0 0 2.71429 -2660.61 -1313.14)

                if (text_retate != null) {

//                    double[] res = getRotateCoord(text_x, text_y, 89.2625f, 1919.81f, 942.577f);
                    canvas.rotate(text_retate[0], text_x, text_y);

//                    canvas.rotate(89.2625f, 1919.81f, 942.577f);
                    canvas.drawText(message, text_x, text_y, paint);
                } else {
                    canvas.drawText(message, text_x, text_y, paint);
                }


                preTag = "";
            }
        }

        @Override
        public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
            if (localName.equals("svg")) {

                floorsFeature.setMaxX(maxX);
                floorsFeature.setMaxY(maxY);
                floorsFeature.setMinX(minX);
                floorsFeature.setMinY(minY);
                Log.d("minX", "" + minX);
                floorsFeature.setFeatures(features);

                if (canvasRestoreCount != null) {
                    canvas.restoreToCount(canvasRestoreCount);
                }
                if (limitsAdjustmentX != null) {
                    limits.left += limitsAdjustmentX;
                    limits.right += limitsAdjustmentX;
                }
                if (limitsAdjustmentY != null) {
                    limits.top += limitsAdjustmentY;
                    limits.bottom += limitsAdjustmentY;
                }
                picture.endRecording();

            } else if (localName.equals("g")) {
                if (boundsMode) {
                    boundsMode = false;
                }
                // Break out of hidden mode
                if (hidden) {
                    hiddenLevel--;
                    // Util.debug("Hidden down: " + hiddenLevel);
                    if (hiddenLevel == 0) {
                        hidden = false;
                    }
                }
                // // Clear gradient map
                // gradientRefMap.clear();
                popTransform();
                fillPaint = fillPaintStack.removeLast();
                fillSet = fillSetStack.removeLast();
                strokePaint = strokePaintStack.removeLast();
                strokeSet = strokeSetStack.removeLast();
                if (!layerAttributeStack.isEmpty()) {
                    layerAttributeStack.removeLast();
                }
            } else if (localName.equals("path")) {

                feature.setMaxWidth(area_maxX - area_minX);
                feature.setMaxHeight(area_maxY - area_minY);

                feature.setMaxX(area_maxX);
                feature.setMaxY(area_maxY);

                feature.setMinX(area_minX);
                feature.setMinY(area_minY);

                if (shape_id == 0 && type_id == 0) {
                    Map<Long, Feature> map = new HashMap<>();
                    map.put(feature_id, feature);
                    features.add(map);
                    strokeNodeList.add(nodes);

                } else {
                    Map<Long, Feature> map = new HashMap<>();
                    map.put(feature_id, feature);
                    features.add(map);
                    feature.setNodes(nodes);
//                    floorsFeature.setStrokeNode(nodes);
                }
            }
        }

//        private static Bitmap getSmallBitMap(Bitmap bitmap, int width, int height) {
//            if (bitmap != null) {
//                float scaleWidth = ((float) width) / bitmap.getWidth();
//                float scaleHeight = ((float) height) / bitmap.getHeight();
//                Matrix matrix = new Matrix();
//                matrix.postScale(scaleWidth, scaleHeight);
//                Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
//                return resizedBitmap;
//            } else {
//                return null;
//            }
//        }

    }

    private static double[] getRotateCoord(float x, float y, float rot_theta, float rot_x, float rot_y) {
        double[] res = {0.0f, 0.0f};

        double d = Math.sqrt(Math.pow((x - rot_x), 2) + Math.pow((y - rot_y), 2));
        // distance from init pos to center

        double ang = Math.atan2(y - rot_y, x - rot_x);

        double new_x = rot_x + Math.cos(ang + (rot_theta / 180) * Math.PI) * d;
        double new_y = rot_y + Math.sin(ang + (rot_theta / 180) * Math.PI) * d;

        res[0] = new_x - x;
        res[1] = new_y - y;

        return res;
    }

    private static float[] getRotateParam(String input_str) {
        //        Pattern pattern = Pattern.compile("rotate/(([0-9/.]*) ([0-9/.]*) ([0-9/.]*)/)");

        Pattern pattern = Pattern.compile("rotate\\(([0-9\\.]+) ([0-9\\.]+) ([0-9\\.]+)\\)");
        Matcher matcher = pattern.matcher(input_str);
        float[] res = {0.0f, 0.0f, 0.0f};
//        System.out.println(matcher);
        int count = matcher.groupCount();
        System.out.println(count);
        if (matcher.find() & count == 3) {
            for (int i = 1; i <= 3; i++) {
                System.out.println(matcher.group(i));
                res[i - 1] = Float.parseFloat(matcher.group(i));
            }
            return res;
        } else {
            System.out.println("Not Found");
            return null;
        }

    }
}
